import { BufferGeometry, CanvasTexture, ClampToEdgeWrapping, ExtrudeGeometry, Material, Mesh, MeshBasicMaterial, NearestFilter, ShaderMaterial, Shape, UVMapping, Vector2 } from "three";
import { CqkjMap } from "../Basic/CqkjMap";
import { MapElement, MapElementOption } from "../Basic/MapElement";
import { OneDimensional, setValue } from "../Util/common";
import { PROJECTION } from "../Util/static";

export interface SpaceVolumeOption extends MapElementOption {
  path: number[][];
  material: ShaderMaterial
}

const min = PROJECTION.altToZ(500)
const max = PROJECTION.altToZ(14000)

export class SpaceVolume extends MapElement<SpaceVolumeOption>{
  mesh: Mesh<BufferGeometry, Material>;
  get show(): boolean {
    return this.option.show
  }
  set show(val: boolean) {
    setValue(this.option, 'show', val, () => {
      this.mesh.visible = val
    })
  }

  constructor(map: CqkjMap, option: SpaceVolumeOption) {
    super(map, option)
    const path = option.path.map(e => [PROJECTION.lonToX(e[0]), PROJECTION.latToY(e[1])]);
    setMask(path, option.material)
    this.mesh = new Mesh(createGeometry(path), option.material)

    this.mesh.position.setZ(min)

    this.mesh.scale.setZ(max - min)

    map.scene.add(this.mesh)
  }

  onDestroy(): void {
    this.map.scene.remove(this.mesh);
    this.mesh.geometry.dispose();
  }

}

function setMask(points: number[][], material: ShaderMaterial) {

  const min = new Vector2(Infinity, Infinity)
  const max = new Vector2(-Infinity, -Infinity)
  points.forEach(p => {
    min.x > p[0] && (min.x = p[0])
    min.y > p[1] && (min.y = p[1])
    max.x < p[0] && (max.x = p[0])
    max.y < p[1] && (max.y = p[1])
  })
  const canvas = document.createElement('canvas')
  canvas.width = 256
  canvas.height = 256
  const x2u = new OneDimensional([min.x, max.x], [8, 248])
  const y2v = new OneDimensional([max.y, min.y], [8, 248])
  const ctx = canvas.getContext('2d')!

  ctx.beginPath()
  ctx.moveTo(x2u.forward(points[0][0]), y2v.forward(points[0][1]))
  for (let i = 1; i < points.length; i++) {
    ctx.lineTo(x2u.forward(points[i][0]), y2v.forward(points[i][1]))
  }
  ctx.closePath()
  ctx.fill()
  material.uniforms.u_min_mask.value.set(x2u.backward(0),y2v.backward(256))
  material.uniforms.u_max_mask.value.set(x2u.backward(256),y2v.backward(0))
  material.uniforms.u_mask.value=new CanvasTexture(canvas,UVMapping, ClampToEdgeWrapping, ClampToEdgeWrapping, NearestFilter, NearestFilter)
}

function createGeometry(points: number[][]) {
  const shape = new Shape(points.map(e => new Vector2(e[0], e[1])))
  const geometry = new ExtrudeGeometry(shape, {
    steps: 1,
    depth: 1,
    bevelEnabled: false
  })
  return geometry

}